﻿//
//  Copyright (c) 2009-2011 Liang Quan Qing.
//  All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions
//  are met:
//  1. Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//  2. Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//
//  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
//  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
//  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
//  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
//  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
//  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
//  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
//  SUCH DAMAGE.
//

static int dbcs_t32( fcnv_t*     pfcnv,
                     const char* srcString,
                     size_t      srcSizeInBytes,
                     DChar_t *   pBuffer,
                     size_t      destSizeInDChars)
{
    priv_t *pd;                                         // the converter information
    size_t  inLength;                                   // the length of input string
    size_t  bcount, dcount;                             // the counter
    WChar_t tchar, schar;
    const unsigned char* pSrc;
    int boLead, chLen;
    fpIsLeadByte_t isLeadByte;

    pSrc = (const unsigned char*)srcString;
    pd = (priv_t*) pfcnv->privdata;
    inLength = (srcSizeInBytes) ? (srcSizeInBytes) : ( strlen(pSrc) + 1 ) ;
    pfcnv->inCount  = 0;
    pfcnv->outCount = 0;
    pfcnv->error    = FCNV_OK;

    if ( pSrc == NULL )                                 // avoid null-poiter
    {
        pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
        return pfcnv->error;
    }

    if ( pBuffer == NULL )                              // pBuffer==NULL, donot convert, calculate target size only
        destSizeInDChars = (size_t)(-1);                //      target size will save into pfcnv->outCount

    isLeadByte = fcnv_GetFp_IsLeadByte(pfcnv);

    bcount = 0;
    dcount = 0;
    while ( bcount < inLength )
    {
        if ( dcount < destSizeInDChars )
        {
            boLead = isLeadByte(&pSrc[bcount]);
            if ( !boLead ) {
                schar = pSrc[bcount];
                chLen = 1;
            }
            else if ( pSrc[bcount+1] ) {
                schar = (pSrc[bcount] << 8) | pSrc[bcount+1];
                chLen = 2;
            }
            else {
                schar = pSrc[bcount] << 8;
                chLen = 1;
            }

            tchar = __btow(pd, schar);
            if ( tchar != 0xffff )
            {
                if ( pBuffer )
                    pBuffer[dcount] = (DChar_t) tchar;
                pfcnv->inCount += chLen;
                pfcnv->outCount ++ ;
                bcount += chLen;
                dcount ++;
                continue;
            }
            else
            {
                if ( pd->boUseDefaultChar )
                {
                    if ( pBuffer )
                        pBuffer[dcount] = (DChar_t) pd->DefaultWChar;
                    pfcnv->inCount += chLen;
                    pfcnv->outCount++;
                    bcount += chLen;
                    dcount ++;
                    continue;
                }
                else
                {
                    pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
                    return -1;
                }
            }
        }
        else
        {
            pfcnv->error = FCNV_ERR_NOT_ENOUGH;
            return -1;
        }
    }
    return 0;
}

static int dbcs_f32( fcnv_t*     pfcnv,
                     const DChar_t*srcString,
                     size_t      srcSizeInDChars,
                     char *      pBuffer,
                     size_t      destSizeInBytes)
{
    priv_t *pd;                                         // the converter information
    size_t  inLength;                                   // the length of input string
    size_t  bcount,dcount;
    int ok;                                             // mean convert OK
    int charlen, k;
    WChar_t wChar;
    unsigned char* pDest;

    pDest = (unsigned char*)pBuffer;
    pd = (priv_t*) pfcnv->privdata;
    inLength = (srcSizeInDChars) ? (srcSizeInDChars) : ( dstrlen(srcString) + 1 ) ;
    pfcnv->inCount  = 0;
    pfcnv->outCount = 0;
    pfcnv->error    = FCNV_OK;

    if ( srcString == NULL )                            // avoid null-poiter
    {
        pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
        return pfcnv->error;
    }

    if ( pDest == NULL )                                // pBuffer==NULL, donot convert, calculate target size only
        destSizeInBytes = (size_t)(-1);                 //      target size will save into pfcnv->outCount

    bcount = 0;
    dcount = 0;
    while ( dcount < inLength )
    {
        if ( srcString[dcount] < 0xffff )
        {
            wChar = __wtob(pd, (unsigned short) srcString[dcount]);
            if ( wChar != 0xffff ) {
                ok = 1;
                if ( wChar >= 0x100 )
                    charlen = 2;
                else
                    charlen = 1;
            }
            else {
                ok = 0;
                charlen = pd->charsize;
            }
        }
        else {
            ok = 0;
            charlen = pd->charsize;
        }
        ///////////
        if ( bcount+charlen < destSizeInBytes )
        {
            if ( ok )
            {
                if ( pDest )
                {
                    if ( wChar < 0x100 )
                        pDest[bcount] = (unsigned char) wChar;
                    else {
                        pDest[bcount] = (unsigned char) (wChar >> 8);
                        pDest[bcount+1] = (unsigned char) (wChar & 0xff);
                    }
                }
                pfcnv->inCount ++;
                dcount ++;
                pfcnv->outCount += charlen;
                bcount += charlen;
                continue;
            }
            else
            {
                if ( pd->boUseDefaultChar )
                {
                    if ( pDest ) {
                        k = 0;
                        while ( k < charlen ) {
                            pDest[bcount+k] = pd->pDefaultChar[k];
                            k++;
                        }
                    }
                    pfcnv->inCount ++;
                    dcount ++;
                    pfcnv->outCount += charlen;
                    bcount += charlen;
                    continue;
                }
                else
                {
                    pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
                    return -1;
                }
            }
        }
        else
        {
            pfcnv->error = FCNV_ERR_NOT_ENOUGH;
            return -1;
        }
    }

    return 0;
}

static int dbcs_t16(fcnv_t*     pfcnv,
                    const char* srcString,
                    size_t      srcSizeInBytes,
                    WChar_t *   pBuffer,
                    size_t      destSizeInWChars)
{
    priv_t *pd;                                         // the converter information
    size_t  inLength;                                   // the length of input string
    size_t  bcount, wcount;                             // the counter
    WChar_t tchar, schar;
    const unsigned char* pSrc;
    fpIsLeadByte_t isLeadByte;
    int boLead, chLen;

    pSrc = (const unsigned char*)srcString;
    pd = (priv_t*) pfcnv->privdata;
    inLength = (srcSizeInBytes) ? (srcSizeInBytes) : ( strlen(pSrc) + 1 ) ;
    pfcnv->inCount  = 0;
    pfcnv->outCount = 0;
    pfcnv->error    = FCNV_OK;

    if ( pSrc == NULL )                                 // avoid null-poiter
    {
        pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
        return pfcnv->error;
    }

    if ( pBuffer == NULL )                              // pBuffer==NULL, donot convert, calculate target size only
        destSizeInWChars = (size_t)(-1);                //      target size will save into pfcnv->outCount

    isLeadByte = fcnv_GetFp_IsLeadByte(pfcnv);

    bcount = wcount = 0;
    while ( bcount < inLength )
    {
        if ( wcount < destSizeInWChars )
        {
            boLead = isLeadByte(&pSrc[bcount]);
            if ( !boLead ) {
                schar = pSrc[bcount];
                chLen = 1;
            }
            else if ( pSrc[bcount+1] ) {
                schar = (pSrc[bcount] << 8) | pSrc[bcount+1];
                chLen = 2;
            }
            else {
                schar = pSrc[bcount] << 8;
                chLen = 1;
            }

            tchar = __btow(pd, schar);
            if ( tchar != 0xffff )
            {
                if ( pBuffer )
                    pBuffer[wcount] = tchar;
                pfcnv->inCount += chLen;
                pfcnv->outCount ++ ;
                bcount += chLen;
                wcount ++;
                continue;
            }
            else
            {
                if ( pd->boUseDefaultChar )
                {
                    if ( pBuffer )
                        pBuffer[wcount] = pd->DefaultWChar;
                    pfcnv->inCount ++ ;
                    pfcnv->outCount++;
                    bcount += chLen;
                    wcount ++;
                    continue;
                }
                else
                {
                    pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
                    return -1;
                }
            }
        }
        else
        {
            pfcnv->error = FCNV_ERR_NOT_ENOUGH;
            return -1;
        }
    }
    return 0;
}

static int dbcs_f16( fcnv_t*     pfcnv,
                     const WChar_t* srcString,
                     size_t      srcSizeInWChars,
                     char *      pBuffer,
                     size_t      destSizeInBytes)
{
    priv_t *pd;                                         // the converter information
    size_t  inLength;                                   // the length of input string
    size_t  bcount,wcount;
    int valid;                                          // mean the UTF-16 surrogate-character is valid
    int utf16width;                                     // the UTF-16 character width
    int ok;                                             // mean convert OK
    int bcharlen, k;
    WChar_t wChar;
    unsigned char* pDest;

    pDest = (unsigned char*)pBuffer;
    pd = (priv_t*) pfcnv->privdata;
    inLength = (srcSizeInWChars) ? (srcSizeInWChars) : ( wstrlen(srcString) + 1 ) ;
    pfcnv->inCount  = 0;
    pfcnv->outCount = 0;
    pfcnv->error    = FCNV_OK;

    if ( srcString == NULL )                            // avoid null-poiter
    {
        pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
        return pfcnv->error;
    }

    if ( pDest == NULL )                                // pBuffer==NULL, donot convert, calculate target size only
        destSizeInBytes = (size_t)(-1);                 //      target size will save into pfcnv->outCount

    bcount = wcount = 0;
    while ( wcount < inLength )
    {
        if ( ! isSurrogate(srcString[wcount]) )         // if is normal UTF-16 character, not surrogate
        {
            wChar = __wtob(pd, srcString[wcount]);
            if ( wChar != 0xffff ) {
                ok = 1;
                if ( wChar >= 0x100 )
                    bcharlen = 2;
                else
                    bcharlen = 1;
            }
            else {
                ok = 0;
                bcharlen = pd->charsize;
            }
            utf16width = 1;
        }
        else
        {
            ok = 0;
            bcharlen = pd->charsize;
            MixSurrogate(srcString[wcount], srcString[wcount+1], &valid);
            if ( valid )
                utf16width = 2;
            else
                utf16width = 1;
        }
        ///////////
        if ( bcount+bcharlen < destSizeInBytes )
        {
            if ( ok )
            {
                if ( pDest )
                {
                    if ( wChar < 0x100 )
                        pDest[bcount] = (unsigned char) wChar;
                    else {
                        pDest[bcount] = (unsigned char) (wChar >> 8);
                        pDest[bcount+1] = (unsigned char) (wChar & 0xff);
                    }
                }
                pfcnv->inCount += utf16width ;
                wcount += utf16width;
                pfcnv->outCount += bcharlen;
                bcount += bcharlen;
                continue;
            }
            else
            {
                if ( pd->boUseDefaultChar )
                {
                    if ( pDest ) {
                        k = 0;
                        while ( k < bcharlen ) {
                            pDest[bcount+k] = pd->pDefaultChar[k];
                            k++;
                        }
                    }
                    pfcnv->inCount += utf16width ;
                    wcount += utf16width;
                    pfcnv->outCount += bcharlen;
                    bcount += bcharlen;
                    continue;
                }
                else
                {
                    pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
                    return -1;
                }
            }
        }
        else
        {
            pfcnv->error = FCNV_ERR_NOT_ENOUGH;
            return -1;
        }
    }

    return 0;
}

static int dbcs_t8( fcnv_t*     pfcnv,
                    const char* srcString,
                    size_t      srcSizeInBytes,
                    char *      pBuffer,
                    size_t      destSizeInBytes)
{
    priv_t *pd;                                         // the converter information
    size_t  inLength;                                   // the length of input string
    size_t  bcount, u8count;                             // the counter
    WChar_t tchar, schar;
    const unsigned char* pSrc;
    unsigned char* pDest;
    int utf8len, i;
    int valid;
    fpIsLeadByte_t isLeadByte;
    int boLead, chLen;
    unsigned char utf8buf[8];

    pSrc = (const unsigned char*)srcString;
    pDest = (unsigned char*) pBuffer;
    pd = (priv_t*) pfcnv->privdata;
    inLength = (srcSizeInBytes) ? (srcSizeInBytes) : ( strlen(pSrc) + 1 ) ;
    pfcnv->inCount  = 0;
    pfcnv->outCount = 0;
    pfcnv->error    = FCNV_OK;

    if ( pSrc == NULL )                                 // avoid null-poiter
    {
        pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
        return pfcnv->error;
    }

    if ( pDest == NULL )                                // pBuffer==NULL, donot convert, calculate target size only
        destSizeInBytes = (size_t)(-1);                 //      target size will save into pfcnv->outCount

    isLeadByte = fcnv_GetFp_IsLeadByte(pfcnv);

    bcount = u8count = 0;
    while ( bcount < inLength )
    {
        boLead = isLeadByte(&pSrc[bcount]);
        if ( !boLead ) {
            schar = pSrc[bcount];
            chLen = 1;
        }
        else if ( pSrc[bcount+1] ) {
            schar = (pSrc[bcount] << 8) | pSrc[bcount+1];
            chLen = 2;
        }
        else {
            schar = pSrc[bcount] << 8;
            chLen = 1;
        }

        tchar = __btow(pd, schar);
        if ( tchar != 0xffff )
            U32CharToU8Buff_1(tchar, utf8buf, &utf8len, &valid);
        else
        {
            if ( pd->boUseDefaultChar )
                U32CharToU8Buff_1(pd->DefaultWChar, utf8buf, &utf8len, &valid);
            else
            {
                valid = 0;
                utf8len = 0;
            }
        }
        ////////
        if ( u8count + utf8len < destSizeInBytes )
        {
            if ( valid )
            {
                if ( pDest ) {
                    for ( i = 0; i < utf8len; i++ )
                        pDest[u8count+i] = utf8buf[i];
                }
                pfcnv->inCount += chLen ;
                bcount += chLen ;
                pfcnv->outCount += utf8len ;
                u8count += utf8len;
                continue;
            }
            else
            {
                pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
                return -1;
            }
        }
        else
        {
            pfcnv->error = FCNV_ERR_NOT_ENOUGH;
            return -1;
        }
    }
    return 0;
}

static int dbcs_f8( fcnv_t*     pfcnv,
                    const char* srcString,
                    size_t      srcSizeInBytes,
                    char *      pBuffer,
                    size_t      destSizeInBytes)
{
    priv_t *pd;
    size_t  inLength;
    size_t  bcount,ucount;
    DChar_t dwChar;
    unsigned char* pDest;
    const unsigned char* pSrc;
    int utf8len, bcharlen, k;
    int valid, ok;
    unsigned short tchar;

    pDest = (unsigned char*)pBuffer;
    pSrc = (const unsigned char*)srcString;
    pd = (priv_t*) pfcnv->privdata;
    inLength = (srcSizeInBytes) ? (srcSizeInBytes) : ( strlen(pSrc) + 1 ) ;
    pfcnv->inCount  = 0;
    pfcnv->outCount = 0;
    pfcnv->error    = FCNV_OK;

    bcount = ucount = 0;
    while ( ucount < inLength )
    {
        U8BuffToU32Char_1(&pSrc[ucount], &utf8len, &dwChar, &valid);
        ok = 0;
        if ( valid && dwChar < 0xffff )
        {
            tchar = __wtob(pd, (unsigned short)dwChar);
            if ( tchar != 0xffff ) {
                ok = 1;
                if ( tchar >= 0x100 )
                    bcharlen = 2;
                else
                    bcharlen = 1;
            }
            else
                bcharlen = pd->charsize;
        }
        else
            bcharlen = pd->charsize;
        //////////////
        if ( bcount+bcharlen < destSizeInBytes )
        {
            if ( ok )
            {
                if ( pDest ) {
                    if ( tchar < 0x100 )
                        pDest[bcount] = (unsigned char) tchar;
                    else {
                        pDest[bcount] = (unsigned char) (tchar >> 8);
                        pDest[bcount+1] = (unsigned char) (tchar & 0xff);
                    }
                }
                pfcnv->inCount += utf8len ;
                ucount += utf8len;
                pfcnv->outCount += bcharlen;
                bcount += bcharlen;
                continue;
            }
            else
            {
                if (pd->boUseDefaultChar)
                {
                    if ( pDest ) {
                        k = 0;
                        while ( k < bcharlen ) {
                            pDest[bcount+k] = pd->pDefaultChar[k];
                            k++;
                        }
                    }
                    pfcnv->inCount += utf8len ;
                    ucount += utf8len;
                    pfcnv->outCount += bcharlen;
                    bcount += bcharlen;
                    continue;
                }
                else
                {
                    pfcnv->error = FCNV_ERR_ILLEGAL_CHARS;
                    return -1;
                }
            }
        }
        else
        {
            pfcnv->error = FCNV_ERR_NOT_ENOUGH;
            return -1;
        }
    }
    return 0;
}

// If mbchar is not NULL and if the object that mbchar points to forms a valid multibyte character,
// mbtowc returns the length in bytes of the multibyte character.
// If mbchar is NULL or the object that it points to is a wide-character null character (L'\0'), the function returns 0.
// If the object that mbchar points to does not form a valid multibyte character within the first count characters, it returns –1.

static int dbcs_mbtowcFunc(fcnv_t* pfcnv, unsigned int * pdchar, const unsigned char* pmbchar, size_t count)
{
    fpIsLeadByte_t isLeadByte;
    priv_t *pd;
    unsigned short tchar, schar;
    int boLead, chLen;

    if ( pmbchar == NULL ) {
        if ( pdchar )
            *pdchar = 0;
        return 0;
    }

    pd = (priv_t*) pfcnv->privdata;
    isLeadByte = fcnv_GetFp_IsLeadByte(pfcnv);

    boLead = isLeadByte(&pmbchar[0]);
    if ( !boLead ) {
        schar = pmbchar[0];
        chLen = 1;
    }
    else if ( pmbchar[1] ) {
        schar = (pmbchar[0] << 8) | pmbchar[1];
        chLen = 2;
    }
    else {
        schar = pmbchar[0] << 8;
        chLen = 1;
    }

    tchar = __btow(pd, schar);
    if ( tchar != 0xffff )
    {
        if ( pdchar )
            *pdchar = (unsigned int) tchar;
        ////////
        if ( tchar )
            return chLen;
        else
            return 0;
    }
    else
        return -1;
}

// If wctomb converts the wide character to a multibyte character, it returns the number of bytes (which is never greater than MB_CUR_MAX) in the wide character.
// If wchar is the wide-character null character (L'\0'), wctomb returns 1. If the target pointer mbchar is NULL, wctomb returns 0.
// If the conversion is not possible in the current locale, wctomb returns –1 and errno is set to EILSEQ.

static int dbcs_wctombFunc(fcnv_t* pfcnv, unsigned char *pmbchar, unsigned int dchar)
{
    priv_t *pd;
    unsigned short c;

    pd = (priv_t*) pfcnv->privdata;

    if (pmbchar == NULL)
        return 0;

    if ( dchar < 0x10000 )
    {
        c = __wtob(pd, (unsigned short)dchar);
        if ( c != 0xffff )
        {
            if ( c < 0x100 )
            {
                *pmbchar = (unsigned char)c;
                return 1;
            }
            else
            {
                *pmbchar = (unsigned char) (c >> 8);
                *(pmbchar+1) = (unsigned char) (c & 0xff);
                return 2;
            }
        }
        else
            return -1;
    }
    else
        return -1;
}










